(ns vigenere
  ;In here i saw how to use :as, because i got warnings when i wanted to use get from clojure.contrib.string : http://comments.gmane.org/gmane.comp.java.clojure.user/46004
  (:require [clojure.contrib.string :as str])
  (:require clojure.java.io))

  
;In here i learnt how to read a file: http://stackoverflow.com/questions/4118123/read-a-very-large-text-file-into-a-list-in-clojure
(def vigenere-sequence
 (with-open [rdr (clojure.java.io/reader "resources/Vigenere.txt")]
                 (vec (line-seq rdr))))


(defn findfirst "Finds appropriate row in vigenere cipher based on key letter." 
  [key-letter vigenere-vector]
  (filter(fn [vigenere-vector]
           (= key-letter(first vigenere-vector)))
         vigenere-vector))


(defn vigenere-square "Creates vigenere square based on keyword." [key-word]
(loop [square {} key-word 
      (str/upper-case key-word)]
      (if (empty? key-word)
       square
          (recur(conj square 
                 {(keyword(str(first key-word)))
                  (vec(first(findfirst (first key-word)
                                 vigenere-sequence)))})
                (rest key-word)))))

(def vigenere-row
  (vec(first vigenere-sequence)))

(defn get-letter-index "Returns first index of letter." [row letter]
  (.indexOf row letter))

(defn get-key-row "Returns vigenere row based on keyword."  [key-word square]
  (square (keyword (str key-word))))

(defn code-vigenere "Codes letter to vigenere cipher." [original key-word square]
  (vigenere-row (get-letter-index 
                            (get-key-row key-word square) original)))
  
(defn decode-vigenere "Decodes letter from vigenere cipher." [original key-word square]
  ((get-key-row key-word square) (get-letter-index vigenere-row original)))

(def cipher-text #"[a-zA-Z]*")


(defn check-letters "Investigates if letter belongs to cipher." [key-word] 
  (re-matches cipher-text key-word))


(defn text-no-special-chars "Returns just letters with their appropriate indexes, so they can be coded/decoded correct." 
  [text] 
  (loop [final-text [] text-chars 
   (str/upper-case text) indeks 0]
   (if (empty? text-chars)
        final-text
        (let [just-letters (check-letters (str (first text-chars)))]
          (recur(if just-letters 
                  (conj final-text 
                [indeks
                (first text-chars)]) final-text) 
                (rest text-chars)
                (if just-letters (+ 1 indeks) indeks))))))


;First i just used pmap and it was good, but then i saw partition on this great article 
;and it really speeded things up:http://www.fatvat.co.uk/2009/05/jvisualvm-and-clojure.html
;The combination of this function and printing function to text area vector by vector
;is, from my experience, faster solution than applying concat to all this vectors and making one string
;of them, and than setting that text to textarea. Like this, this function is really
;fast even for big texts, but printing to text area depends on size of the text.
(defn map-in-parallel "Mapping all letters in parallel based on their index and keyword. 
                       The bigger the text is, the more usefull this function will be, since 
                       it partitions text in chunks of 5000 letters and processors are working at full speed."
  [text key-word mapping]
  (let [square (vigenere-square key-word) 
        key-word-big (str/upper-case key-word)]
  (pmap (fn map-chunk [chunks] 
          (map (fn map-letter [letter]
           (mapping (nth letter 1) 
               (str/get key-word-big (mod (nth letter 0)(.length key-word))) 
                square)) chunks))
        (vec(partition-all 5000 (text-no-special-chars text))))))